{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第 8 章：卷积神经网络 — PyTorch"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 导入 MNIST 数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torchvision\n",
    "import torchvision.transforms as transforms\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "transform = transforms.Compose(\n",
    "    [transforms.ToTensor()])\n",
    "\n",
    "# 训练集\n",
    "trainset = torchvision.datasets.MNIST(root='./datasets/ch08/pytorch',     # 选择数据的根目录\n",
    "                                      train=True,\n",
    "                                      download=True,    # 不从网络上download图片\n",
    "                                      transform=transform)\n",
    "trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,\n",
    "                                         shuffle=True, num_workers=2)\n",
    "# 测试集\n",
    "testset = torchvision.datasets.MNIST(root='./datasets/ch08/pytorch',     # 选择数据的根目录\n",
    "                                     train=False,\n",
    "                                     download=True,    # 不从网络上download图片\n",
    "                                     transform=transform)\n",
    "testloader = torch.utils.data.DataLoader(testset, batch_size=4,\n",
    "                                        shuffle=False, num_workers=2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dataset MNIST\n",
      "    Number of datapoints: 60000\n",
      "    Split: train\n",
      "    Root Location: ./datasets/ch08/pytorch\n",
      "    Transforms (if any): Compose(\n",
      "                             ToTensor()\n",
      "                         )\n",
      "    Target Transforms (if any): None\n"
     ]
    }
   ],
   "source": [
    "print(trainset)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dataset MNIST\n",
      "    Number of datapoints: 10000\n",
      "    Split: test\n",
      "    Root Location: ./datasets/ch08/pytorch\n",
      "    Transforms (if any): Compose(\n",
      "                             ToTensor()\n",
      "                         )\n",
      "    Target Transforms (if any): None\n"
     ]
    }
   ],
   "source": [
    "print(testset)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来展示一些训练样本图像"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXYAAAB6CAYAAACr63iqAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAElRJREFUeJzt3XmQVNXZx/HvIyiJWwBxYYugEgV3VIpXLU3hhiuJimI0olJFueDrVlHEJS5vVPSNr0tpFEFFYwQkLqh5X0FwX1AQNSogGBGQQeKOS6ng8/7R9545I91MT09Pz/Sd36eK6qdPd9977tz2ePrcc59j7o6IiGTHOs1dARERKS817CIiGaOGXUQkY9Swi4hkjBp2EZGMUcMuIpIxathFRDKmUQ27mQ00s/lmttDMRparUiIiUjor9QYlM2sDvAscACwFXgWOc/d3ylc9ERFpqLaN+Gw/YKG7/wvAzCYAg4CCDbuZ6TZXEZGG+9jdNy32zY0ZiukKLImeL03K6jCz4WY2y8xmNWJfIiKt2QcNeXNjeuyWp2yNHrm7jwHGgHrsIiKV0Jge+1Kge/S8G7CscdUREZHGakzD/irQy8x6mtl6wBBgSnmqJSIipSp5KMbdV5nZCOAJoA1wp7u/XbaaiYhISUqe7ljSzjTGLiJSitnuvnuxb9adpyIiGaOGXUQkY9Swi4hkjBp2EZGMUcMuIpIxathFRDJGDbuISMY0JleMSKNsueWWIb788ssBmDhxYigzq01H1K9fvxA/99xzACxatCiUvffee01VTWkhRowYEeKbb74ZgEL34UyePDnEn376KQBff/11KJs3b17ez02dOhWADz5oUM6tFkc9dhGRjNGdp9Lk1l9//RCPHz8+xAMGDAhxhw4dGrzdtHcFcNJJJ4V4+fLlDd6WtHwvv/xyiPfYY48m2ccZZ5wBwG233dYk228E3XkqItKaqWEXEckYXTyVslpnndq+whFHHAHAn/70p1DWu3fvEK9atSrE7777LlD3otdjjz0W4h49eoT4oosuAuDAAw8MZVdffXWITz755JLrL63bddddB8DSpUtDWfw9rBbqsYuIZIwadhGRjNFQjJRV166165k/+OCDa7wezx8+55xzQvzEE0+sdbvxjIhtttkGgCuuuKLkerZEgwcPDvHYsWNDvPHGG4f4hRdeAGD48OGh7J133qlA7VqHdAbXsGHDQtlTTz0V4ngufEumHruISMaoYRcRyZhWfYPSQQcdBMCoUaNC2U477RTiZ599NsQjR44EYO7cuRWqXXVq06ZNiI855pg1Xn/44YdD/O2335a0j3QoZtasWaHszTffDPE+++xT0nYrKf47pbOG/vCHP4SyOJ3CkiVLQty9e3cApkypXTf+/PPPD3FNTU2IV65cWcYaN7/DDz88xKNHjwbgV7/6VSj74YcfQvz++++HeKONNgKgS5cuJe23Z8+eIV68eHFJ2yiD8t6gZGZ3mtkKM3srKutoZtPMbEHy2PDbBkVEpEnU22M3s32Ar4B73H2HpOxa4FN3v8bMRgId3P2CenfWAnrsl112WYgPOeQQAB5//PFQNmPGjBD/7ne/C3Ha+7z00ktD2S233NJU1ZS1yNdjnzBhQohPPfXUitepoeK59uPGjQNg9erVoeziiy8OcXx7+9/+9jcADj744Lzbveuuu0IcXwDMqrPPPjvEX3zxRYjjv8O2224LwGGHHRbK0l/rAPvtt99a95HJHru7Pwt8+pPiQUCa9GM88JuiqyciIk2q1OmOm7t7DYC715jZZoXeaGbDgeGFXhcRkfJq8nns7j4GGAOVHYqJ51PfcMMNIe7UqVOI06GWhQsX5t1GmvcbarMSPvTQQ6HsjjvuCPH333/fyBpLsdLMfvH87tmzZzdXdYr2i1/8IsTxUEsqHlYoNMw3Z84coPBQzKabbtqYKlad+L/tQubPn1/nEaB9+/Yhrm8ophqVOt3xIzPrDJA8rihflUREpDFKbdinAEOTeCjwSHmqIyIijVXvUIyZ3Q/8GuhkZkuBPwLXAJPMbBiwGBhceAvN46abbgpxPL81ngv71VdfFb29dHmtzTffPJQNGjQoxA888EBJ9ZSGS+dtf/bZZ6Es/pndUsWzK+L4888/B+qmESgkvb09vvdCmlY8RHbuuec2Y02KV2/D7u7HFXgpewNTIiIZoJQCIiIZk7nsjrvssgtQu8gD1L3tuCHDL7E08f5HH30UyuJb1zUU07QuuKD2/rcdd9wRqHsjUpz+oaXq06dP3vIbb7wRgO+++67ebcQ32eRTzDakYdJFYKqJeuwiIhmTuR77BhtsANRdqT5OCJRP27a1f4Z4ubUjjzwyxL169QJgiy22CGUvvfRS4yoraxXP+z7vvPNCnC6/98wzz1S8To2x22675S2PUyPUp0OHNdMyxUsM/vnPf254xVqJNNc61P73XIx//OMfTVGdJqUeu4hIxqhhFxHJmMwNxSxbtgyoe5EzvvU/Xkasc+fOAAwYMCCUvfHGGyG++eabQ/z6668DdefHd+vWrVzVlkSaOxtg0qRJIY5vlU+HZRYsWFC5ipVZfJEzvTBfqvjCcbyEoOSkWVzj+ehZTCMQU49dRCRj1LCLiGRM5oZi0hkwJ5xwQiiLf4IdddRRIX711VeBunOD49k0+cQzMeLFEq699toSayyx+++/P8QHHHBAiJ9//vkQx0Nk1eStt8IiZLRr1y7E6UIQ8TBgPFNr6NChIf7tb3+7xnarcZ51U9t///1DPHnyZKDu37w+8fftyy+/LF/FKkQ9dhGRjFHDLiKSMZkbiknNmzcvxOVcAzNe+X3PPfcs23ZbsjijZToUEM8kKjQ7aOrUqUDtjCKAKVOmhHi99dYLcbpGZXyDWLyG5emnnx7iOFtnNZk+fXqI02yhUDvTKh5miW/OGjhwYAVqV/3izK333ntviBsyBJMumHPnnXeGsjT7ZjVRj11EJGMy22OXhtt6661DfNVVV4V45513DnGcUK0+/fv3X6PsyiuvDHG/fv1CnK4av2jRolDWt2/fEFdjr+mn4hXuTzvttBBfeumlABx77LGhLJ7nPmPGjBBvv/32QN3UFq3ZfffdF+L4gml8P0RDpN+zRx6pXTsoTWEB8OOPP5a03UpTj11EJGPUsIuIZIyGYlqp+KfqK6+8AkCXLl1CWTxf//bbb1/j8/GQSfzeNLsmwDXXXAPAccfVLsJ1ySWX5K1Pur04x30Whl8KifP3P/744wB07NgxlK1evTrENTU1Ib777rsBOPHEE5u4hi3PZpttFuL0+AcPrl2Vs02bNmXbxyeffBLK4mHJ+IJ+aty4cSGOl2tsTvX22M2su5k9ZWZzzextMzsrKe9oZtPMbEHyuGY+URERqbhihmJWAee5e2+gP3CGmfUBRgLT3b0XMD15LiIizayYxaxrgJokXmlmc4GuwCDg18nbxgNPAxfk2URmffjhh81dhZKNHj06xOkt7ffcc08oO+mkk4re1rrrrhvieAZNPMumPunfstSlC6vZN998U+dR8hs7dmyIDz300Irtd9SoUWt9PZ7hFN9jEc/YiWeDVUKDxtjNrAewKzAT2Dxp9HH3GjPbrMBnhgPDG1dNEREpVtENu5ltCPwdONvdvzSzoj7n7mOAMck2vJRKiohI8Ypq2M1sXXKN+n3u/mBS/JGZdU56652BFU1VyZbqtddea+4qlCzOnJiaNm1a0Z/v3r17iOMZGvl+cqZZNAE23HDDEPfu3TvEPXv2BGCvvfYKZelsEamVLqoR/8333Xff5qpORcWzYlqSHj165C2PZ4Ols5mWLFlSgRoVNyvGgHHAXHe/PnppCpDmEx0KPPLTz4qISOUV02PfC/g98E8zS7M5jQKuASaZ2TBgMTC4wOelSsRJlOKV2eNkZ2nu+rjHv9VWW4V4zpw5IX700UeBujnW0yRLAH/9619DnN42/+STT5Z+AK1AmtM9/jtut912IR40aFCI49vipfLSSQkA7du3ByrXYy9mVszzQKEB9WwvHCgiUoWUUkBEJGOUUqCVilezT+ebH3300aFs7733DnGcaiD18ccfhzhNHQBw/fXX531PPvGFUilOmv7h6aefDmVxDvsjjzwyxFkbikmXvQTYY489mnRfy5YtC3GnTp1CHK8h0JKpxy4ikjFq2EVEMkZDMa3UpEmTQrxq1aqiPzd37lyg7tJh9Q25SPnNmjUrxPFQTJ8+fUKcLgkXL9pRzUaMGBHidPbUrbfeGsrats3fnJ111lkAfPvtt0XvK17OsVevXiGOs5fmc8opp4Q430IzlaIeu4hIxqhhFxHJGHOvXPqWLOSKiX/2Hn/88SEeOnRovreLNIlNNtkkxDNnzgxxfLNYesOZUjNkwmx3373YN6vHLiKSMbp42gi//OUvm7sK0krFS7eNGTMmxPE9BUOGDAFg+fLloWz27NkVqJ00N/XYRUQyRg27iEjGaCimgVauXBnivn37hjjOM94al3eT5jNx4sQQx1k304v7L730UijTUEzroB67iEjGqGEXEckYzWNvhPhn7ZlnnhniF198sTmqIyLZpXnsIiKtmRp2EZGMqXcoxsx+BjwLtCM3i2ayu//RzHoCE4COwGvA7939+8Jbyt5QjIhIhZR9KOY7YIC77wzsAgw0s/7AaOB/3L0X8BkwrJTaiohIedXbsHtOOjF73eSfAwOAyUn5eOA3TVJDERFpkKLG2M2sjZm9DqwApgHvAZ+7e7pCw1Kga9NUUUREGqKoht3dV7v7LkA3oB/QO9/b8n3WzIab2Swzm5XvdRERKa8GzYpx98+Bp4H+QHszS1MSdAOWFfjMGHffvSED/yIiUrp6G3Yz29TM2ifxz4H9gbnAU8DRyduGAo80VSVFRKR4xSQB6wyMN7M25P5HMMndHzOzd4AJZvZfwBxgXBPWU0REilTplAL/Br4GsrqsfSd0bNVIx1adWtOxbenumxb74Yo27ABmNiur4+06tuqkY6tOOrbClFJARCRj1LCLiGRMczTsY+p/S9XSsVUnHVt10rEVUPExdhERaVoaihERyRg17CIiGVPRht3MBprZfDNbaGYjK7nvcjOz7mb2lJnNNbO3zeyspLyjmU0zswXJY4fmrmspksRvc8zsseR5TzObmRzXRDNbr7nrWAoza29mk81sXnLu/iND5+yc5Lv4lpndb2Y/q9bzZmZ3mtkKM3srKst7niznpqRdedPM+jZfzetX4NiuS76Tb5rZQ+nd/slrFybHNt/MDipmHxVr2JM7V28BDgb6AMeZWZ9K7b8JrALOc/fe5HLnnJEcz0hgepKnfnryvBqdRS51RCor+fdvBP7P3bcDdiZ3jFV/zsysK/CfwO7uvgPQBhhC9Z63u4GBPykrdJ4OBnol/4YDf6lQHUt1N2se2zRgB3ffCXgXuBAgaVOGANsnn7k1aUvXqpI99n7AQnf/V7LS0gRgUAX3X1buXuPuryXxSnINRFdyxzQ+eVtV5qk3s27AocDY5LmRgfz7ZrYxsA9J+gt3/z5JbFf15yzRFvh5kpxvfaCGKj1v7v4s8OlPigudp0HAPcnaES+TS1DYuTI1bbh8x+buU6M06C+TS6wIuWOb4O7fufv7wEJybelaVbJh7wosiZ5nJoe7mfUAdgVmApu7ew3kGn9gs+arWcluAM4Hfkyeb0I28u9vBfwbuCsZZhprZhuQgXPm7h8C/w0sJtegfwHMJhvnLVXoPGWtbTkF+N8kLunYKtmwW56yqp9raWYbAn8Hznb3L5u7Po1lZocBK9x9dlyc563VeO7aAn2Bv7j7ruTyFlXdsEs+yXjzIKAn0AXYgNwQxU9V43mrT1a+n5jZReSGee9Li/K8rd5jq2TDvhToHj0vmMO9WpjZuuQa9fvc/cGk+KP0Z2DyuKK56leivYAjzGwRueGyAeR68EXl32/hlgJL3X1m8nwyuYa+2s8Z5NJpv+/u/3b3H4AHgT3JxnlLFTpPmWhbzGwocBhwvNfeYFTSsVWyYX8V6JVcpV+P3AWBKRXcf1kl487jgLnufn300hRy+emhCvPUu/uF7t7N3XuQO0cz3P14MpB/392XA0vMbNukaD/gHar8nCUWA/3NbP3ku5keW9Wft0ih8zQFODGZHdMf+CIdsqkWZjYQuAA4wt2/iV6aAgwxs3Zm1pPcBeJX6t2gu1fsH3AIuSu+7wEXVXLfTXAse5P7SfQm8Hry7xBy49HTgQXJY8fmrmsjjvHXwGNJvFXyhVoIPAC0a+76lXhMuwCzkvP2MNAhK+cMuByYB7wF3Au0q9bzBtxP7lrBD+R6rcMKnSdywxW3JO3KP8nNDGr2Y2jgsS0kN5aetiW3Re+/KDm2+cDBxexDKQVERDJGd56KiGSMGnYRkYxRwy4ikjFq2EVEMkYNu4hIxqhhFxHJGDXsIiIZ8/8aIlzCxxtQlAAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "          9           2           9           2\n"
     ]
    }
   ],
   "source": [
    "def imshow(img):\n",
    "    npimg = img.numpy()\n",
    "    plt.imshow(np.transpose(npimg, (1, 2, 0)))\n",
    "\n",
    "# 选择一个 batch 的图片\n",
    "dataiter = iter(trainloader)\n",
    "images, labels = dataiter.next()\n",
    "\n",
    "# 显示图片\n",
    "imshow(torchvision.utils.make_grid(images))\n",
    "plt.show()\n",
    "# 打印 labels\n",
    "print(' '.join('%11s' % labels[j].numpy() for j in range(4)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义卷积神经网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Net(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        self.conv1 = nn.Conv2d(1, 6, 5)        # 1个输入图片通道，6个输出通道，5x5 卷积核\n",
    "        self.pool1 = nn.MaxPool2d(2, 2)        # max pooling，2x2\n",
    "        self.conv2 = nn.Conv2d(6, 16, 5)       # 6个输入图片通道，16个输出通道，5x5 卷积核\n",
    "        self.pool2 = nn.MaxPool2d(2,2)         # max pooling，2x2\n",
    "        self.fc1 = nn.Linear(16 * 4 * 4, 120)  # 拉伸成一维向量，全连接层\n",
    "        self.fc2 = nn.Linear(120, 84)          # 全连接层 \n",
    "        self.fc3 = nn.Linear(84, 10)           # 全连接层，输出层 softmax，10个数字\n",
    "    \n",
    "    def forward(self, x):\n",
    "        x = F.relu(self.conv1(x))\n",
    "        x = self.pool1(x)\n",
    "        x = F.relu(self.conv2(x))\n",
    "        x = self.pool2(x)\n",
    "        x = x.view(-1, 16 * 4 * 4)    # 拉伸成一维向量\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = F.relu(self.fc2(x))\n",
    "        x = self.fc3(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Net(\n",
      "  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))\n",
      "  (pool1): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))\n",
      "  (pool2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
      "  (fc1): Linear(in_features=256, out_features=120, bias=True)\n",
      "  (fc2): Linear(in_features=120, out_features=84, bias=True)\n",
      "  (fc3): Linear(in_features=84, out_features=10, bias=True)\n",
      ")\n"
     ]
    }
   ],
   "source": [
    "net = Net()\n",
    "print(net) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义损失函数与优化算法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "criterion = nn.CrossEntropyLoss()    # 交叉熵损失\n",
    "optimizer = optim.Adam(net.parameters(), lr=0.0001)    # Adam 优化算法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练网络"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[epoch: 1, mini-batch:  2000] loss: 1.085\n",
      "[epoch: 1, mini-batch:  4000] loss: 0.397\n",
      "[epoch: 1, mini-batch:  6000] loss: 0.305\n",
      "[epoch: 1, mini-batch:  8000] loss: 0.231\n",
      "[epoch: 1, mini-batch: 10000] loss: 0.191\n",
      "[epoch: 1, mini-batch: 12000] loss: 0.174\n",
      "[epoch: 1, mini-batch: 14000] loss: 0.152\n",
      "[epoch: 2, mini-batch:  2000] loss: 0.133\n",
      "[epoch: 2, mini-batch:  4000] loss: 0.119\n",
      "[epoch: 2, mini-batch:  6000] loss: 0.125\n",
      "[epoch: 2, mini-batch:  8000] loss: 0.102\n",
      "[epoch: 2, mini-batch: 10000] loss: 0.110\n",
      "[epoch: 2, mini-batch: 12000] loss: 0.094\n",
      "[epoch: 2, mini-batch: 14000] loss: 0.097\n",
      "[epoch: 3, mini-batch:  2000] loss: 0.084\n",
      "[epoch: 3, mini-batch:  4000] loss: 0.087\n",
      "[epoch: 3, mini-batch:  6000] loss: 0.078\n",
      "[epoch: 3, mini-batch:  8000] loss: 0.070\n",
      "[epoch: 3, mini-batch: 10000] loss: 0.086\n",
      "[epoch: 3, mini-batch: 12000] loss: 0.073\n",
      "[epoch: 3, mini-batch: 14000] loss: 0.079\n",
      "[epoch: 4, mini-batch:  2000] loss: 0.059\n",
      "[epoch: 4, mini-batch:  4000] loss: 0.070\n",
      "[epoch: 4, mini-batch:  6000] loss: 0.070\n",
      "[epoch: 4, mini-batch:  8000] loss: 0.055\n",
      "[epoch: 4, mini-batch: 10000] loss: 0.057\n",
      "[epoch: 4, mini-batch: 12000] loss: 0.066\n",
      "[epoch: 4, mini-batch: 14000] loss: 0.061\n",
      "[epoch: 5, mini-batch:  2000] loss: 0.052\n",
      "[epoch: 5, mini-batch:  4000] loss: 0.053\n",
      "[epoch: 5, mini-batch:  6000] loss: 0.052\n",
      "[epoch: 5, mini-batch:  8000] loss: 0.059\n",
      "[epoch: 5, mini-batch: 10000] loss: 0.047\n",
      "[epoch: 5, mini-batch: 12000] loss: 0.049\n",
      "[epoch: 5, mini-batch: 14000] loss: 0.047\n"
     ]
    }
   ],
   "source": [
    "num_epoches = 5    # 设置 epoch 数目\n",
    "cost = []     # 损失函数累加\n",
    "\n",
    "for epoch in range(num_epoches):    \n",
    "    \n",
    "    running_loss = 0.0\n",
    "    for i, data in enumerate(trainloader):\n",
    "        # 输入样本和标签\n",
    "        inputs, labels = data\n",
    "        \n",
    "        # 每次训练梯度清零\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        # 正向传播、反向传播和优化过程\n",
    "        outputs = net(inputs)\n",
    "        loss = criterion(outputs, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "        # 打印训练情况\n",
    "        running_loss += loss.item()\n",
    "        if (i+1) % 2000 == 0:    # 每隔2000 mini-batches，打印一次\n",
    "            print('[epoch: %d, mini-batch: %5d] loss: %.3f' % \n",
    "                 (epoch + 1, i + 1, running_loss / 2000))\n",
    "            cost.append(running_loss / 2000)\n",
    "            running_loss = 0.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(cost)\n",
    "plt.xlabel('mini-batches(per 2000)')\n",
    "plt.ylabel('running_loss')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 测试数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on the 60000 train images: 98.883 %\n"
     ]
    }
   ],
   "source": [
    "correct = 0\n",
    "total = 0\n",
    "with torch.no_grad():\n",
    "    for data in trainloader:\n",
    "        images, labels = data\n",
    "        outputs = net(images)\n",
    "        _, predicted = torch.max(outputs.data, 1)\n",
    "        total += labels.size(0)\n",
    "        correct += (predicted == labels).sum().item()\n",
    "\n",
    "print('Accuracy on the 60000 train images: %.3f %%' % \n",
    "     (100 * correct / total))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on the 10000 test images: 98.580 %\n"
     ]
    }
   ],
   "source": [
    "correct = 0\n",
    "total = 0\n",
    "with torch.no_grad():\n",
    "    for data in testloader:\n",
    "        images, labels = data\n",
    "        outputs = net(images)\n",
    "        _, predicted = torch.max(outputs.data, 1)\n",
    "        total += labels.size(0)\n",
    "        correct += (predicted == labels).sum().item()\n",
    "\n",
    "print('Accuracy on the 10000 test images: %.3f %%' % \n",
    "     (100 * correct / total))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
